﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using Chada.Infrastructure.Utility;
using System.IO;
using System.Reflection;
using System.Web;
using System.Security.Cryptography;
using Chada.Infrastructure.Utility.DEncrypt;
using System.Text.RegularExpressions;
namespace Chada.Infrastructure
{
    public class Common
    {

        /// <summary>
        /// 生成32位序列号
        /// 提示：identify参数必须唯一，建议使用身份证号、手机号等作为用户标识。
        ///       如果要创建一个永远可用的序列化，那么用户标识不应该使用身份证号、手机号等，需绑定部署服务器上服务器CPU序列号等。 
        ///       以防这个序列号在互联网上传播落入他人手中。
        /// 重要说明：生成的序列号前4位不保存信息：前两位用于序列号校验、第三位用于指示标识号是否应该与服务器上服务器CPU序列号去匹配、第四位预留
        /// </summary>
        public static string GenerateSerialNumber(uint onlineUserCount, uint userCount, DateTime expiration, string identify, bool bindHardwareCode)
        {
            HashAlgorithm hash = new FNV1_32();
            byte[] identifyHashed = hash.ComputeHash(Encoding.ASCII.GetBytes(identify));

            //string ih_x16 = BitConverter.ToString(identifyHashed).Replace("-", "");
            //string onlineUserCount_x16 = BitConverter.ToString(BitConverter.GetBytes(onlineUserCount));
            //string userCount_x16 = BitConverter.ToString(BitConverter.GetBytes(userCount));
            //string expiration_x16 = BitConverter.ToString(BitConverter.GetBytes(Convert.ToUInt32(expiration.ToString("yyyyMMdd"))));

            //注意：十六进制不区分大小写ABCDE的
            string ih_x16 = BitConverter.ToUInt32(identifyHashed, 0).ToString("x").PadLeft(8, '0');
            string onlineUserCount_x16 = onlineUserCount.ToString("x").PadLeft(8, '0');
            string userCount_x16 = userCount.ToString("x").PadLeft(8, '0');
            string expiration_x16 = Convert.ToUInt32(expiration.ToString("yyyyMMdd")).ToString("x").PadLeft(8, '0');

            //简单校验处理（onlineUserCount_x16 前两位是校验位）
            string hexStr = CycK(onlineUserCount_x16, userCount_x16, expiration_x16, ih_x16);
            onlineUserCount_x16 = hexStr + Convert.ToInt32(bindHardwareCode) + "0" + onlineUserCount_x16.Substring(4);

            string ept = string.Format("{0}{1}{2}{3}", onlineUserCount_x16, userCount_x16, expiration_x16, ih_x16);
            return ept;
        }

        /// <summary>
        /// 解析序列号
        /// </summary>
        /// <param name="serialNumber">序列号</param>
        /// <returns>返回值格式：10,20,20150101,15306209803,1 (分别代表 在线用户数、用户数、日期、用户标识、用户标识是否绑定了服务器CPU序列号)</returns>
        public static string ParseSerialNumber(string serialNumber)
        {
            string str1 = serialNumber.Substring(0, 8);
            string str2 = serialNumber.Substring(8, 8);
            string str3 = serialNumber.Substring(16, 8);
            string str4 = serialNumber.Substring(24, 8);

            string hexStr = CycK(str1, str2, str3, str4);
            if (serialNumber.Substring(0, 2) != hexStr)
            {
                throw new Exception("无效的序列号");
            }

            uint a = ByteConverter.HexConvertToUInt32(str1.Substring(4));
            uint b = ByteConverter.HexConvertToUInt32(str2);
            uint c = ByteConverter.HexConvertToUInt32(str3);
            string d = str4;
            char e = str1[2];

            if (e != '1' && e != '0')
            {
                throw new Exception("无效的序列号");
            }
            if (e == '1' && !IsMatchHardwareCode(str4))
            {
                throw new Exception("无效的序列号。");
            }

            return string.Format("{0},{1},{2},{3},{4}", a, b, c, d, e);
        }

        private static string CycK(string onlineUserCount_x16, string userCount_x16, string expiration_x16, string ih_x16)
        {
            HashAlgorithm hash = new FNV1_32();
            uint i1 = BitConverter.ToUInt32(hash.ComputeHash(Encoding.ASCII.GetBytes(onlineUserCount_x16.Substring(4))), 0);
            uint i2 = BitConverter.ToUInt32(hash.ComputeHash(Encoding.ASCII.GetBytes(userCount_x16)), 0);
            uint i3 = BitConverter.ToUInt32(hash.ComputeHash(Encoding.ASCII.GetBytes(expiration_x16)), 0);
            uint i4 = BitConverter.ToUInt32(hash.ComputeHash(Encoding.ASCII.GetBytes(ih_x16)), 0);
            byte ci = (byte)(i1 + i2 + i3 + i4);
            return ci.ToString("x").PadLeft(2, '0');
        }

        /// <summary>
        /// 将序列号标识与服务器CPU序列号比较
        /// </summary>
        public static bool IsMatchHardwareCode(string hashedIdentify)
        {
            byte[] a = BitConverter.GetBytes(ByteConverter.HexConvertToUInt32(hashedIdentify));

            string cpuSN = HardwareInfo.GetCPUSerialNumber();
            HashAlgorithm hash = new FNV1_32();
             byte[] b = hash.ComputeHash(Encoding.ASCII.GetBytes(cpuSN));

            return ByteConverter.Equal(a, b);
        }
    }
}